package ace.cmp.spring.graphql.data.jdbc.querydsl.core.controller;

import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.security.SecurityManager;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.security.impl.SecurityManagerImpl;
import ace.cmp.security.api.SecurityService;
import ace.cmp.spring.graphql.core.model.GenericPageContent;
import ace.cmp.spring.graphql.data.jdbc.querydsl.core.controller.model.PageInput;
import graphql.schema.DataFetchingEnvironment;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.security.access.prepost.PreAuthorize;

/**
 * @author caspar
 * @date 2023/4/12 17:30 特性：
 * 1、常用管理端graphql接口：新增、修改、删除、搜索
 */
@Slf4j
public abstract class AbstractGenericAdminGraphql<TEntity extends GenericPageContent<TId>, TId extends Serializable, TPageInput extends PageInput>
    extends AbstractGenericGraphql<TEntity, TId, TPageInput> implements InitializingBean {

  @Autowired
  @Getter(AccessLevel.PROTECTED)
  @Setter(AccessLevel.PROTECTED)
  private SecurityService securityService;

  @Getter(AccessLevel.PROTECTED)
  @Setter(AccessLevel.PROTECTED)
  private SecurityManager securityManager;

  @PreAuthorize("permitAll()")
  @MutationMapping("Save")
  @Override
  public TId save(
      @Valid @NotNull @Argument("input") TEntity entity,
      @Valid @NotNull @Argument("saveNullFieldFlag") Boolean saveNullFieldFlag) {
    this.getSecurityManager().checkWriteAuthority();
    return super.save(entity, saveNullFieldFlag);
  }

  @PreAuthorize("permitAll()")
  @MutationMapping("Add")
  @Override
  public TId add(
      @Valid @NotNull @Argument("input") TEntity entity,
      @Valid @NotNull @Argument("saveNullFieldFlag") Boolean saveNullFieldFlag) {
    this.getSecurityManager().checkWriteAuthority();
    return super.add(entity, saveNullFieldFlag);
  }

  @PreAuthorize("permitAll()")
  @MutationMapping("Modify")
  @Override
  public TId modify(
      @Valid @NotNull @Argument("input") TEntity entity,
      @Valid @NotNull @Argument("saveNullFieldFlag") Boolean saveNullFieldFlag) {
    this.getSecurityManager().checkWriteAuthority();
    return super.modify(entity, saveNullFieldFlag);
  }

  @PreAuthorize("permitAll()")
  @MutationMapping("Remove")
  @Override
  public Boolean remove(@Valid @NotEmpty @Argument("ids") List<TId> ids) {
    this.getSecurityManager().checkRemoveAuthority();
    return super.remove(ids);
  }

  @PreAuthorize("permitAll()")
  @MutationMapping("RemoveLogic")
  @Override
  public Boolean removeLogic(@Valid @NotEmpty @Argument("ids") List<TId> ids) {
    this.getSecurityManager().checkRemoveAuthority();
    return super.removeLogic(ids);
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    super.afterPropertiesSet();
    securityManager = this.initSecurityManager();
  }

  @PreAuthorize("permitAll()")
  @QueryMapping("Get")
  @Override
  public CompletableFuture<TEntity> getById(
      @Valid @NotNull @Argument("id") TId id, DataFetchingEnvironment dataFetchingEnvironment) {
    this.getSecurityManager().checkReadAuthority();
    return super.getById(id, dataFetchingEnvironment);
  }

  @PreAuthorize("permitAll()")
  @QueryMapping("GetList")
  @Override
  public CompletableFuture<List<TEntity>> getListByIdIn(
      @Valid @NotNull @NotEmpty @Argument("ids") List<TId> ids,
      DataFetchingEnvironment dataFetchingEnvironment) {
    this.getSecurityManager().checkReadAuthority();
    return super.getListByIdIn(ids, dataFetchingEnvironment);
  }

  @PreAuthorize("permitAll()")
  @QueryMapping("Search")
  @Override
  public Page<TEntity> search(
      @Argument(value = "query") String query, @Valid @NotNull @Argument("page") TPageInput page) {
    this.getSecurityManager().checkWriteAuthority();
    Page<TEntity> result = super.search(query, page);
    return result;
  }

  protected boolean hasAuthority(String authority) {
    return this.securityService.hasAuthority(authority);
  }

  protected SecurityManager initSecurityManager() {
    return new SecurityManagerImpl(this.getEntityClass(), this::hasAuthority);
  }
}
